/* NoX (NoC Simulator)
 *
 * Dept. of Computer Science & Engineering, Pennsylvania State University.
 * All Rights Reserved.
 *  
 * 1. License     
 * NoX is distributed free of charge for academic, educational, noncommercial 
 * research purposes as long as this notice in its entirety is preserved in
 * every file included in this package.
 * All commercial use of this program requires separate licence. Contact the
 * author for details.
 * 
 * 2. All the publications that used the simulation results generated by the 
 * NoX should notify the author of the publication information and put 
 * following reference.
 *
 *  http://www.cse.psu.edu/~dpark/nox/
 * 
 * 3. Modification of the source code is permitted and encouraged as long as 
 * it follows the terms described in this copyright notice.
 *
 * 4. The author is not responsible for any problems caused by possible errors
 * of the NoX package. Therefore, users should verify the simulation result
 * before using it in their publication.
 *
 * Dept. of Computer Science & Engineering, Pennsylvania State University.
 * Contact: dpark@cse.psu.edu 
 * 
 * 6. If problems are found with the NoX package, please send an email to the
 * author for discussion and correction.

*/

/* Update History
 *
 *
 */

/* APP.C - Generate Message */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#ifdef TR_INTEG
#include "transaction.h"
#include "globals.h"
#undef INVALID
#endif

#include "main.h"
#include "router.h"
#include "flit.h"
#include "flit_move.h"
#include "router_common.h"
#include "app.h"
#include "nic.h"
#include "multimedia.h"
#include "shared.h"
#include "sim_result.h"
#include "batch.h"

#include <vector>
extern std::vector<source_queue_t> source_queue[MAX_NODES]; 

int hot_spot[4] = {0,8,55,63};
// Needed for the Self-Similar traffic
double pareto(double k, double alpha)
{
  double dum;

  do
    dum = ((double)rand()/(double)RAND_MAX);
  while (dum == 0.0);

  return (k * pow(dum, -1/alpha));
}

bool app_to_nic_output_buf_stage(int src, int dest, int is_nack, long long id, int len)
{
  // Generate messages and send them to the NIC output buf.


  int node, pc, dest_pc, vc, pos, dnode, snode, i, src_PE, dst_PE;
  int inj_flag, vc_found;
  int sx, sy, dx, dy, vc_set;
  int is_usable_vc[MAX_NIC_VC];
  flit_t *flit_ptr, *flit_ptr2;

  static int mpeg_num[MAX_NODES]={0};
  static int f_num[MAX_NODES]={0};
  static int flit_num=0;
  static int last_selected_vc[MAX_NODES][MAX_NIC_PC]={0};
  static int interval_cnt    [MAX_NODES][MAX_NIC_PC]={0};
  static int load_usage      [MAX_NODES][MAX_NIC_PC]={0};

#ifdef TR_INTEG
  long long trid = id;
#endif

  for(node=0; node<NUM_NODES; node++)
  {
    int r,c;
    r=node/NUM_COLS;
    c=node%NUM_COLS;

    //If simulating two networks first half of the network is address network
    //and will inject requests, reposnes are simulated in second half of the
    //network
    if(two_networks == YES && RESP_RQ_TRAFFIC && node == NUM_NODES/2)
      break;

    //Only local routers inject in hybrid topology
    if(hybrid_topology == YES && router_info[node].type == GLOBAL)
      break;


    // Check whether src, dest is assigned by caller.
    // This happens in E2E retransmission.
    // If they're not specified, this function is called for general 
    // message injection (from inside main() function) 

    //Injecting from traces or pepsi
    if(src != -1 && dest != -1)
    {
      //Direct network src address is same as router address
      if(NUM_PE == NUM_NODES)
        snode = src;
      //Indirect network(like cmesh, xbar, bus, bfly, hybrids)
      else
      {
        snode = topology == BFLY ? src/bfly.k : src/concentration_degree;
        snode = (topology == XBAR || topology == BUS) ? node : snode;
      }
      if(snode != node) 
        continue;
    }

    // Prevent unhealthy nodes from generating messages.
    if(router[node].health_status == TRAPPED || router[node].health_status == FAIL)
      continue;

    NUM_NIC_PC = router_info[node].num_nic_pc;
    NUM_PC = router_info[node].num_pc;

    for(pc=0; pc<NUM_NIC_PC; pc++)
    {

      // We generate messages with the rate of 'inj_rate' per each physical channel.
      // But, if all the NIC output bufs of the VCs are full, we cannot generate 
      // message from this injection channel.

      if(src != -1 && dest != -1)
      {
        if(NUM_PE != NUM_NODES)
        {
          snode = topology == BFLY ? src%bfly.k : src%concentration_degree;
          snode = (topology == XBAR || topology == BUS) ? src : snode;
          if(snode != pc)
            continue;
        }
      }

      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      //1. Decide whether to inject or not according to load rate or special traffic
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/

      if(src != -1 && dest != -1)
        inj_flag = VALID;


      else if(synthetic_trace_enabled)
      {
        if(active_source  == -1 || (active_source != -1 && node == active_source))
          inj_flag = VALID;
        else
          inj_flag = INVALID;
      }

      else if(in_traffic == UNIFORM)
        inj_flag = (  ((double)rand()/(double)RAND_MAX)  < load_rate_per_source[node] )? VALID:INVALID;
      // inj_flag = (  ((double)rand()/(double)RAND_MAX)  < load_rate/(double)MSG_LEN  )? VALID:INVALID;


      else if(in_traffic == MULTIMEDIA || in_traffic == SSIMILAR)
      {

        if(--interval_cnt[node][pc] > 0) 
          continue;

        if( ((float)(load_usage[node][pc]*MSG_LEN)/(float)(sim_clock+1)) >= load_rate_per_source[node])
          continue;

        if(in_traffic == MULTIMEDIA)
        {
          // If first time, randomly assign mpeg video out of 7 choices.
          if(mpeg_num[node] == 0)
            mpeg_num[node] = (int)((double)((MPEG_VIDEO_NUM-1)*rand())/(double)RAND_MAX) + 1; // 1~7

          // retrieve interval count.
          interval_cnt[node][pc] = mpeg_generate(mpeg_num[node], f_num[node]++);
        }
        else if(in_traffic == SSIMILAR)
        {
          //interval_cnt[node][pc] = (int)pareto( ((double)MSG_LEN/load_rate)/30, 1.1 );
          interval_cnt[node][pc] = (int)pareto( (load_rate_per_source[node])/30, 1.1 );
          fflush(stdout);
        }

        inj_flag = VALID;
        load_usage[node][pc]++;
      }

      // Generate message only when inj_flag == VALID
      if(inj_flag != VALID)
        continue;


      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      //2. Generate Source - Destination Pair according traffic pattern.
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/

      if(dest == -1)
      {
        //----------------------------------------
        // Choose a destination node from trace file.
        if(synthetic_trace_enabled)
        {
          if(inj_flag == INVALID)
            continue;

          if(trace_buffer[node][trace_ptr[node]].clock <= sim_clock - trace_clock_recycle[node])
            dnode = trace_buffer[node][trace_ptr[node]].dst;
          else
            continue;
        }
        //----------------------------------------
        // Choose a destination node by formula
        else
        {
          switch(pattern_per_source[node])
          {
            case NR : // Normal Random
              {
                do // Randomly generate destination node.
                {
                  int range = NUM_PE;
                  if(two_networks == YES)
                    range = NUM_PE/2;

                  dnode = (int)( (double)(range) * (rand() / (double)RAND_MAX));

                  if(NUM_NODES != NUM_PE)
                  {
                    //Indirect Network
                    if(topology == XBAR || topology == BUS)
                    { dest_pc = dnode;dnode = node; }
                    else if(topology == BFLY)
                    { dest_pc = dnode%bfly.k;dnode = dnode/bfly.k;}
                    else if(concentrated == YES)
                    { dest_pc = dnode%concentration_degree;dnode = dnode/concentration_degree; }
                  }

                  // Destination node cannot be the same as source node and should be active (not trapped or failed)
                } while (/*dnode == node ||*/ router[dnode].health_status == TRAPPED || router[dnode].health_status == FAIL);

                break;
              }
            case TP : // Transpose
              {
                int tmpx = node % NUM_COLS; int tmpy = node / NUM_COLS;
                dnode = tmpx * NUM_COLS + tmpy;
                break;
              }
            case TN : // Tornado
              {
                int tmpx = node % NUM_COLS; int tmpy = node / NUM_COLS; int k=5;
                tmpx = (tmpx + (k-1)/2) % NUM_COLS;
                dnode = tmpy * NUM_COLS + tmpx;
                break; 
              }
            case BC : // Bit-Complement
              {
                int tmpx = node % NUM_COLS; int tmpy = node / NUM_COLS; int k=4;
                tmpx = (k-tmpx-1) % NUM_COLS; tmpy = (k-tmpy-1) % NUM_ROWS;
                if(tmpx < 0) tmpx += NUM_COLS;
                if(tmpy < 0) tmpy += NUM_ROWS;
                dnode = tmpy * NUM_COLS + tmpx;
                break; 
              }
            case NB : // Nearest Neighbor
              {
                if(NUM_NODES == NUM_PE)
                {
                  do{
                    do{
                      //int port = (int)( (double)(3* rand()) / (double)(RAND_MAX));
                      int port = (int)(rand() % (NUM_PC-NUM_NIC_PC));
                      dnode = neighbor[node][port];
                    } while(dnode == EDGE);
                  }while(router[dnode].health_status == TRAPPED || router[dnode].health_status == FAIL); 
                }
                else
                {
                  int cdegree = topology == BFLY ? bfly.k : concentration_degree;
                  dnode = (int)( (double)(cdegree) * (rand() / (double)RAND_MAX));
                  //Indirect Network
                  if(topology == XBAR || topology == BUS)
                  { dest_pc = dnode;dnode = node; }
                  else if(topology == BFLY)
                  { dest_pc = dnode;dnode = node;}
                  else if(concentrated == YES)
                  { dest_pc = dnode; dnode = node; }
                }
                break; 
              }
            case LC :
              {
                int local = (double) (rand() / (double)RAND_MAX) <= local_traffic_ratio == YES;
                if(local == YES)
                {
                  if(NUM_NODES == NUM_PE)
                  {
                    do{
                      do{
                        //int port = (int)( (double)(3* rand()) / (double)(RAND_MAX));
                        int port = (int)(rand() % (NUM_PC-NUM_NIC_PC));
                        dnode = neighbor[node][port];
                      } while(dnode == EDGE);
                    }while(router[dnode].health_status == TRAPPED || router[dnode].health_status == FAIL); 
                  }
                  else
                  {
                    int cdegree = topology == BFLY ? bfly.k : concentration_degree;
                    dnode = (int)( (double)(cdegree) * (rand() / (double)RAND_MAX));
                    //Indirect Network
                    if(topology == XBAR || topology == BUS)
                    { dest_pc = dnode;dnode = node; }
                    else if(topology == BFLY)
                    { dest_pc = dnode;dnode = node;}
                    else if(concentrated == YES)
                    { dest_pc = dnode; dnode = node; }
                  }

                }
                else
                {
                  do // Randomly generate destination node.
                  {
                    int range = NUM_PE;
                    if(two_networks == YES)
                      range = NUM_PE/2;

                    dnode = (int)( (double)(range) * (rand() / (double)RAND_MAX));

                    if(NUM_NODES != NUM_PE)
                    {
                      //Indirect Network
                      if(topology == XBAR || topology == BUS)
                      { dest_pc = dnode;dnode = node; }
                      else if(topology == BFLY)
                      { dest_pc = dnode%bfly.k;dnode = dnode/bfly.k;}
                      else if(concentrated == YES)
                      { dest_pc = dnode%concentration_degree;dnode = dnode/concentration_degree; }
                    }

                    // Destination node cannot be the same as source node and should be active (not trapped or failed)
                  } while (/*dnode == node ||*/ router[dnode].health_status == TRAPPED || router[dnode].health_status == FAIL);

                }
                break;
              }
            case HT : // Hot Spot
              {
                int n = (int)(rand() % 4);
                dnode = NUM_NODES -1;//hot_spot[3];
                break; 
              }

          }// switch
        }
      }
      //-----------------------------------------------------------------
      // No need to choose destination already sent by pepsi or CMP traces
      else
      {
        //Direct network
        if(NUM_PE == NUM_NODES)
          dnode = dest;
        else
        {//Indirect Network
          if(topology == XBAR || topology == BUS)
          { dnode = node; dest_pc = dest;}
          else if(topology == BFLY)
          { dnode = dest/bfly.k; dest_pc = dest%bfly.k;}
          else if(concentrated == YES)
          { dnode = dest/concentration_degree; dest_pc = dest%concentration_degree;}
        }
      }

      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      //3. Now, source and destination pair has been chosen.
      // It's time to check whether there are available VCs for this.
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      // Mark which VCs this pair can use.
      calc_coord(node,  &sx, &sy);
      calc_coord(dnode, &dx, &dy);
      
      int procID= -1, PacketPriority=0, app_inj_tm=sim_clock;
#ifdef TR_INTEG
      TRANSTYPE *tr= NULL;
      if(trid  != -1)
      {
        tr= map_transaction(trid);
        procID = tr->tid;
        app_inj_tm = tr->enter_time;
      }
#endif

      //VC Assignment
#ifdef TR_INTEG
      vc = procID;
      vc_found = NO;
      if( is_ready[node][pc+(NUM_PC-NUM_NIC_PC)][vc].noutbuf == YES)
        vc_found = YES;
#else
      vc_found = NO;
      for(int vc_index=1; vc_index<=NUM_NIC_VC; vc_index++)
      {
        vc = (last_selected_vc[node][pc] + vc_index) % NUM_NIC_VC;
        if( is_ready[node][pc+(NUM_PC-NUM_NIC_PC)][vc].noutbuf == YES)
        {
          vc_found = YES;
          last_selected_vc[node][pc] = vc;
          break;
        }
      }//end for
#endif


      // No buffers are available now. Skip this PC.
      if(vc_found == NO)
      {
        if(verbose == YES)
          printf("Dropping msg %d --> %d len:%d no vc available\n",node,dnode,len);
        continue;
      }


      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      //Sanity Check with message lengths
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      int msglen  = len;
      // Now, we have everything set. Inject message.
#ifdef TR_INTEG
      if(len > 40 || len < 1)
      {
        printf("FATAL ERR injecting packet to NoX exceeding buffer length %d\n",len);
        tr->hist();
        exit(1);
      }
#endif
      if(hybrid_topology == YES)
      {
        msglen = (int)ceil((double)len*mratio);
        global_msg_len = len;
        local_msg_len  =  msglen;
        if(msglen == 0)
        {
          printf("fatal err!! injecting msg of length zero\n");
          exit(1);
        }
      }

      //Direct network
      if(NUM_PE == NUM_NODES)
      { src_PE = node; dst_PE = dnode;}
      else
      {//Indirect Network
        if(topology == XBAR || topology == BUS)
        { src_PE = pc; dst_PE = dest_pc;}
        else if(topology == BFLY)
        { src_PE = node *bfly.k + pc; 
          dst_PE = dnode*bfly.k + dest_pc;}
        else if(concentrated == YES)
        { src_PE = node * concentration_degree + pc; 
          dst_PE = dnode * concentration_degree + dest_pc;}
      }


      float slack=0;

      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      //Source Queue logic
      if(slack_based_arbitration)
      {
        float offset,curr_expected,max_older_slack;
        if(source_queue_logic_enabled)
        {
          offset = source_queue[src_PE].size() ? sim_clock - source_queue[src_PE][0].inj_tm : 0;
          curr_expected = (BaseRRLatency[src_PE]/BaseRRHop[src_PE]) * calc_dist(src_PE,dst_PE);
          if(slack_per_hop_latency != -1)
            curr_expected = slack_per_hop_latency * calc_dist(src_PE,dst_PE);
          max_older_slack = source_queue_max_slack(src_PE);
          slack = (offset + max_older_slack) > curr_expected ? offset + max_older_slack : curr_expected;
          if(slack < 1)
            slack = 1;
        }
#ifdef TR_INTEG
        else
          slack = tr->slack;
#endif
        if(!source_queue_add(src_PE,num_inj_msg+1,false,last_batch_id[node],slack))
        {
          vc_found = NO; 
          continue;
        }
        if(verbose == YES)
          printf("curr expected:%.2f slack : %.2f hops:%d offset:%.2f max old slack:%.2f\n",curr_expected,slack,calc_dist(src_PE,dst_PE),offset,max_older_slack);
      }      
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/

      for(pos=0; pos<msglen; pos++)
      {
        // Set common information
        flit_ptr                = flit_alloc();
        flit_ptr->inj_tm        = sim_clock; 
        flit_ptr->msg_inj_tm    = sim_clock;    
        flit_ptr->app_inj_tm    =  app_inj_tm;    
        flit_ptr->vc_num        = vc;
        flit_ptr->error         = ERROR_NONE;
        flit_ptr->error_penalty = 0;
        flit_ptr->e2e_penalty   = 0;
        flit_ptr->blk_chk_done  = NO;
        flit_ptr->pos           = pos;
        flit_ptr->flit_num      = flit_num++;
        flit_ptr->delay_cycle   = 0;
        flit_ptr->interference_cycles   = 0;
        flit_ptr->bus_latency   = 0;
        flit_ptr->entry_time    = sim_clock;
        flit_ptr->num_hop       = 0;
        flit_ptr->llen          = msglen;
        flit_ptr->msglen        = len;
        flit_ptr->msgtype       = DATA;
        flit_ptr->is_nack       = is_nack;
        flit_ptr->data.inj_pc   = pc;
        flit_ptr->short_flit    = NO;
        flit_ptr->batch_id      = -1;
        flit_ptr->packet_id     = num_inj_msg+1;
#ifdef TR_INTEG
        flit_ptr->trid          = trid;
#endif
        num_inj_flit++;

        // Set flit-type specific information
        if(len == 1)  
          flit_ptr->msgtype    = CONTROL;

        //=======================================================================
        // XShare support 
        //=======================================================================
        if(xshare == YES)
        {
          if(RESP_RQ_TRAFFIC)
          {
            if(flit_ptr->msgtype == CONTROL && src==-1 && dest == -1) 
              flit_ptr->short_flit = YES;
            else
              flit_ptr->short_flit = ((double)(rand() / (double)RAND_MAX)) < short_flit_ratio ? YES:NO;
          }
          else
            flit_ptr->short_flit = ((double)(rand() / (double)RAND_MAX)) < short_flit_ratio ? YES:NO;
        }
        //=======================================================================
        // XShare end support 
        //=======================================================================

        if(pos == 0)
        {
          // Head flit
          //flit_ptr->flit_type = HEAD_FLIT;
          flit_ptr->flit_type = HEAD;
          if(msglen == 1)
            flit_ptr->flit_type = (HEAD | TAIL);

          // Now, set is_sx(y)_less_than_dx(y) for deadlock freedom for TORUS network.
          if(topology == TORUS)
          { 
            flit_ptr->is_sx_less_than_dx = (sx  < dx)? YES:NO; 
            flit_ptr->is_sy_less_than_dy = (sy  < dy)? YES:NO; 
          }// if TORUS

          // Do we need following variable?
          flit_ptr->data.ejt_pc = 0; // Temporarily set eject physical channel number to 0.
          if(procID != -1)
          PacketPriority = FindPriority(procID,node);
        }
        else if(pos == msglen-1)
        {
          // Tail flit
          flit_ptr->flit_type = TAIL;
        }
        else
        {
          // Middle flit
          flit_ptr->flit_type = MIDL;
        }
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
        // priotity arbiteration support
        flit_ptr->priority_id     = node;
        flit_ptr->marking_weight  = 1;
        flit_ptr->priority        = global_priority_matrix[node][node];
        if(slack_based_arbitration)
          flit_ptr->slack = slack;
#ifdef TR_INTEG
        flit_ptr->priority_id =  procID;
        flit_ptr->priority    =  PacketPriority;
        flit_ptr->batch_id    =  last_batch_id[node];
#endif
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/


        // Following two lines are added for 2-stage architecture to simplify code.
        flit_ptr->data.snode = node;
        flit_ptr->data.dnode = dnode;
        flit_ptr->data.dest  = dst_PE;
        flit_ptr->data.src   = src_PE;

        if(sql == YES)
          fprintf(fsql, "INSERT INTO FLIT VALUES(%d,%d,%d,%d);\n", flit_ptr->flit_num, node, dnode, pos);

        if(verbose == YES) 
        //if(flit_ptr->priority_id == 0 && (HEAD_FLIT))
        { 
#ifdef TR_INTEG
          printf("Inject  [%d][%d][%d]-flit:%d, node:%d -> dnode:%d src_PE:%d -> dst_PE:%d dest:%d msglen:%d at clock:%lld type:%s pri:%d pri id:%d pos:%s trid:%lld\n", node, pc+NUM_PC-NUM_NIC_PC, 
              vc, flit_ptr->flit_num, node, dnode, src_PE, dst_PE,flit_ptr->data.dest,msglen, (long long)sim_clock, flit_ptr->msgtype == CONTROL ? "C":"D",
              flit_ptr->priority,flit_ptr->priority_id,pos==0?"H":(pos==msglen-1?"T":"M"),flit_ptr->trid); fflush(stdout);
          print_mbox(&(nic_output_buf[node][pc][vc]));
#else
          printf("Inject  [%d][%d][%d]-flit:%d, node:%d -> dnode:%d src_PE:%d -> dst_PE:%d dest:%d msglen:%d at clock:%lld type:%s\n", node, pc+NUM_PC-NUM_NIC_PC, 
              vc, flit_ptr->flit_num, node, dnode, src_PE, dst_PE,flit_ptr->data.dest,msglen, (long long)sim_clock, flit_ptr->msgtype == CONTROL ? "C":"D"); fflush(stdout);
#endif
        }


        // Now, inject this flit to the NIC output buf.
        send_flit(&(nic_output_buf[node][pc][vc]), &flit_ptr);


        if(synthetic_trace_enabled && pos == msglen-1)
        {
          trace_ptr[node]++;
          if(trace_ptr[node] > 7000)
          {
            trace_ptr[node] = 0;
            trace_clock_recycle[node] = sim_clock;
          }
        }


        // Keep track of the activity.
        if(sim_clock > warmup_cycle)
          total_activity[node]++;

        // Add power consumed by buffer slot.
        p_buffer += PD_BUFFER;

      }// for pos

      // Add power consumed by buffer slot.
      p_buffer += PD_BUFFER;

      num_inj_msg++;


      if(hybrid_topology == YES)
      {
        flit_num += global_msg_len - local_msg_len;
        num_inj_flit += global_msg_len - local_msg_len; 
      }

      // Set last_selected_vc to this vc so that this can utilize the VCs.
      last_selected_vc[node][pc] = vc;

      // If in the E2E retransmission process, return after generating a message.
      if(src != -1 || dest != -1)
        return true;

      // To allow only one message generation per physical injection channel,
      // set inj_flag to zero since we already generated one message.
      inj_flag = INVALID;

    }// for pc
  }// for node
#ifdef TR_INTEG
  return false;
#endif
  return false;
}// nic_injection_state()


bool source_queue_add(int n, unsigned int id, bool done, int bid, float slack)
{
  int size = source_queue[n].size();
  if(size < OT_JOB_SIZE[n])
  {
    source_queue[n].push_back(source_queue_t(id, bid, done,slack));
    source_jobs[n]++;
    if(verbose == YES)
      printf("adding packet at source queue of node:%d id:%d clock:%d\n",n,id,sim_clock);
    return true;
  }
  else
    return false;

}

bool source_queue_update(int n, unsigned int id, bool done)
{
  int size = source_queue[n].size();
  for(int i = 0; i < size; i++)
  {
    if(source_queue[n][i].id == id)
    {
      source_queue[n][i].done = done;
      if(verbose == YES)
        printf("updating packet at source queue of node:%d id:%d clock:%d\n",n,source_queue[n][i].id,sim_clock);
      return true;
    }
  }
  return true;
}

float source_queue_max_slack(int n)
{
  int size = source_queue[n].size();
  float max_slack = 0;
  for(int i = 0; i < size; i++)
  {
    if(source_queue[n][i].slack > max_slack)
      max_slack = source_queue[n][i].slack;
  }
  return max_slack;
}

bool source_queue_delete(int n)
{
  int size = source_queue[n].size();
  if(size)
    if(source_queue[n][0].done == true)
    {
      if(verbose == YES)
        printf("deleting packet at source queue of node:%d id:%d\n",n,source_queue[n][0].id);
      source_queue[n].erase(source_queue[n].begin());
      // if(sim_clock > WARMUP_CYCLES)
      source_jobs_done[n]++;

      if(source_jobs_done[n] == msgs_to_eject[n] + warmup_msgs[n])
      {
        source_sim_done++;
        printf("********************RESULTS for SOURCE %d ***** Sources Done %d ******************************\n",n,source_sim_done);
        printf("- Source [%d] Jobs[%.0lf] Jobs per Cycle / Load Rate /Traffic Pattern : %.4lf / %.3f / %s / %.4lf\n",      
            n,source_jobs_done[n],source_jobs_done[n]/(double)(sim_clock), /*source_jobs[n]/(double)(sim_clock)*/
            load_rate_per_source[n], tp[pattern_per_source[n]], source_job_delay[n]/source_jobs_done[n]) ;
        //sim_result();
        source_throughput[n] = round((source_jobs_done[n]/(double)(sim_clock))*10000)/(double)10000;
        source_job_latency[n] = round((source_job_delay[n]/source_jobs_done[n])*10000)/(double)10000;
        source_job_completion[n] = (double)sim_clock/1000;


        if(source_sim_done == NUM_NODES)
          sim_end_flag = VALID; // End simulation. 
        if(active_source != -1)
          sim_end_flag = VALID; // End simulation when the active source(and only source) completes.

      }
      return true;
    }
  return false;
}

source_queue_t::source_queue_t(unsigned int packet_id, int bid, bool status,float s)
{
  id=packet_id;
  done=status;
  batch_id = bid;
  slack = s;
  inj_tm = sim_clock;
}

